﻿using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace ToolUtil
{
    /// <summary>
    /// # 进程间通信(共享内存)
    /// 
    /// 共享内存的方式进行进程间通信，首先要建立一个进程间共享的内存地址，创建好共享内存地址后，一个进程向地址中写入数据，另外的进程从地址中读取数据。
    /// 在数据的读写的过程中要进行进程间的同步。
    /// 
    /// 进程间数据同步常见的方法有：
    /// 1.共享内存（内存映射文件，共享内存DLL）。2.命名管道和匿名管道。3.发送消息 
    /// </summary>
    public class ProcessShareData
    {
        public string m_str=string.Empty;
        private Semaphore m_Write;  //可写的信号
        private Semaphore m_Read;   //可读的信号
        private Semaphore x_Write;  //可写的信号
        private Semaphore x_Read;   //可读的信号
        private IntPtr m_handle;    //文件句柄
        private IntPtr m_addr;      //共享内存地址
        private IntPtr x_handle;    //文件句柄
        private IntPtr x_addr;      //共享内存地址
        uint mapLength;             //共享内存长

        Thread threadRed;           //线程用来读取数据

        #region 内存操作的函数

        const int INVALID_HANDLE_VALUE = -1;
        const int PAGE_READWRITE = 0x04;
        const int FILE_MAP_ALL_ACCESS = 0x0002;
        const int FILE_MAP_WRITE = 0x0002;

        [DllImport("User32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        //共享内存
        [DllImport("Kernel32.dll", EntryPoint = "CreateFileMapping")]
        private static extern IntPtr CreateFileMapping(IntPtr hFile, //HANDLE hFile,
         UInt32 lpAttributes,                                        //LPSECURITY_ATTRIBUTES lpAttributes,  //0
         UInt32 flProtect,                                           //DWORD flProtect
         UInt32 dwMaximumSizeHigh,                                   //DWORD dwMaximumSizeHigh,
         UInt32 dwMaximumSizeLow,                                    //DWORD dwMaximumSizeLow,
         string lpName                                               //LPCTSTR lpName
         );

        [DllImport("Kernel32.dll", EntryPoint = "OpenFileMapping")]
        private static extern IntPtr OpenFileMapping(
         UInt32 dwDesiredAccess,        //DWORD dwDesiredAccess,
         int bInheritHandle,            //BOOL bInheritHandle,
         string lpName                  //LPCTSTR lpName
         );


        [DllImport("Kernel32.dll", EntryPoint = "MapViewOfFile")]
        private static extern IntPtr MapViewOfFile(
         IntPtr hFileMappingObject,     //HANDLE hFileMappingObject,
         UInt32 dwDesiredAccess,        //DWORD dwDesiredAccess
         UInt32 dwFileOffsetHight,      //DWORD dwFileOffsetHigh,
         UInt32 dwFileOffsetLow,        //DWORD dwFileOffsetLow,
         UInt32 dwNumberOfBytesToMap    //SIZE_T dwNumberOfBytesToMap
         );

        [DllImport("Kernel32.dll", EntryPoint = "UnmapViewOfFile")]
        private static extern int UnmapViewOfFile(IntPtr lpBaseAddress);

        [DllImport("Kernel32.dll", EntryPoint = "CloseHandle")]
        private static extern int CloseHandle(IntPtr hObject);

        #endregion

        public ProcessShareData()
        {
            mapLength = 1024;
        }
        public void close()
        {
            if (threadRed!=null) 
                threadRed.Abort();
            int i = UnmapViewOfFile(m_addr);
            int k = CloseHandle(m_handle);
            UnmapViewOfFile(x_addr);
            CloseHandle(x_handle);
        }
        public void reclose()
        {
            try
            {
                if (threadRed != null)
                    threadRed.Abort();
                int i = UnmapViewOfFile(m_addr);
                int k = CloseHandle(m_handle);
                UnmapViewOfFile(x_addr);
                CloseHandle(x_handle);
            }
            catch { }
        }
        ///<summary>
        /// 初始化共享内存数据 创建一个共享内存
        ///</summary>
        public void init()
        {
            IntPtr hFile;
            m_str = string.Empty;
            m_Write = new Semaphore(1, 1, "WriteMap");  //开始的时候有一个可以写
            m_Read = new Semaphore(0, 1, "ReadMap");    //没有数据可读

            hFile = new IntPtr(INVALID_HANDLE_VALUE);
            m_handle = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, mapLength, "shareMemory");
            m_addr = MapViewOfFile(m_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            threadRed = new Thread(new ThreadStart(ThreadReceive));
            threadRed.Start();
        }
        //不安全的代码在项目生成的选项中选中允许不安全代码
        static unsafe void ByteCopy(byte[] dst, IntPtr src)
        {
            fixed (byte* pDst = dst)
            {
                byte* pdst = pDst;
                byte* psrc = (byte*)src;
                while ((*pdst++ = *psrc++) != '\0')
                    ;
            }
        }
        /// <summary>
        /// 线程启动从共享内存中获取数据信息 
        /// </summary>
        private void ThreadReceive()
        {
            byte[] byteStr;
            string str;
            try
            {
                //读取共享内存中的数据：
                m_Read.WaitOne();//是否有数据写过来
                //Console.WriteLine("接收到发送过来的数据:");
                byteStr = new byte[mapLength];
                ByteCopy(byteStr, m_addr);
                str = Encoding.Default.GetString(byteStr, 0, byteStr.Length);
                if (str[0] != '\0')
                {
                    //Console.WriteLine(str.Contains('\0') ? str.Substring(0, str.IndexOf('\0')) : str);
                    m_str = str.Contains('\0') ? str.Substring(0, str.IndexOf('\0')) : str;
                }
                else
                    Console.WriteLine("Data Emptry, Error!");
                //调用数据处理方法 处理读取到的数据
                m_Write.Release();
            }
            catch (WaitHandleCannotBeOpenedException ex)
            {
                Thread.ResetAbort();
                Console.WriteLine(ex.Message+"qwe");
                //Thread.Sleep(0);
            }
        }
        #region 向进程共享内存中写入数据（B模式运行的进程）

        /// <summary>
        /// B进程：向共享内存中写入的数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void WriteData2SharedMemory(string readedBuffer,string ex)
        {
            byte[] sendStr;
            try
            {
                x_Write = Semaphore.OpenExisting("SerGdlis");
                x_Read = Semaphore.OpenExisting("GdlisNet");
                x_handle = OpenFileMapping(FILE_MAP_WRITE, 0, "shareSerGdlis");
                x_addr = MapViewOfFile(x_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
                Console.WriteLine("B Process Memory Address Operating:" + x_addr);
                // 等待写的信号量
                try
                {
                    x_Write.WaitOne();
                    sendStr = Encoding.Default.GetBytes(readedBuffer + '\0');
                    //如果要是超长的话，应另外处理，最好是分配足够的内存
                    if (sendStr.Length < mapLength)
                        CopyByte2Memory(sendStr, x_addr);
                    x_Read.Release();
                }
                catch (WaitHandleCannotBeOpenedException)
                {}
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                //Console.WriteLine("不存在系统信号量!");
                return;
            }
        }
        /// <summary>
        /// B进程：向共享内存中写入的数据
        /// </summary>
        /// <param name="readedBuffer">写入的数据</param>
        /// <param name="xw"></param>
        /// <param name="xr"></param>
        /// <param name="xh">共享内存池</param>
        public void WriteData2Shared(string readedBuffer, string xw, string xr,string xh)
        {
            byte[] sendStr;
            try
            {
                x_Write = Semaphore.OpenExisting(xw);
                x_Read = Semaphore.OpenExisting(xr);
                x_handle = OpenFileMapping(FILE_MAP_WRITE, 0, xh);
                x_addr = MapViewOfFile(x_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
                //Console.WriteLine("B Process Memory Address Operating:" + x_addr);
                // 等待写的信号量
                try
                {
                    x_Write.WaitOne();
                    sendStr = Encoding.Default.GetBytes(readedBuffer + '\0');
                    //如果要是超长的话，应另外处理，最好是分配足够的内存
                    if (sendStr.Length < mapLength)
                        CopyByte2Memory(sendStr, x_addr);

                    x_Read.Release();
                }
                catch (WaitHandleCannotBeOpenedException)
                {

                }
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                Console.WriteLine("不存在系统信号量!");
                return;
            }
        }
        static unsafe void CopyByte2Memory(byte[] byteSrc, IntPtr dst)
        {
            fixed (byte* pSrc = byteSrc)
            {
                byte* pDst = (byte*)dst;
                byte* psrc = pSrc;
                for (int i = 0; i < byteSrc.Length; i++)
                {
                    *pDst = *psrc;
                    pDst++;
                    psrc++;
                }
            }
        }


        #endregion
    }
}
